#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace Weazel.Gezel.Model
{
  /// <summary>
  /// The purpose of this class is to ensure
  /// that all AbstractEntity parameterized 
  /// instances shares the same instance of
  /// the idSequence variable thus generating
  /// unique id's across the set of actual
  /// classes regardless of the value of their 
  /// parameter.
  /// </summary>
  public abstract class CommonEntity
  {
    /// <summary>
    /// The id sequence counters to pick
    /// new values from.
    /// </summary>
    protected static int idSequence = 0;

    /// <summary>
    /// The name of the entity.
    /// </summary>
    protected string localName;

    /// <summary>
    /// Returns the local name of the component. The
    /// local name is not guaranteed to be unique as
    /// is the case with the fully qualified name
    /// </summary>
    /// <value></value>
    public string LocalName
    {
      get
      {
        return localName;
      }
    }

    internal abstract CommonEntity ParentAsCommonEntity
    {
      get;
    }

    protected Position startPosition;
		public Position StartPosition
    {
      get
      {
        return startPosition;
      }
    }

		protected Position endPosition;
		public Position EndPosition
		{
			get
			{
				return endPosition;
			}
			set
			{
				this.endPosition = value;
			}
		}

    /// <summary>
    /// The unique id of this entity.
    /// </summary>
    protected readonly int id;
    public int Id
    {
      get
      {
        return id;
      }
    }

    protected CommonEntity()
    {
      this.id = idSequence++;
    }
  }

  /// <summary>
  /// The AbstractEntity class is the common base
  /// class for all the entities found in a gezel
  /// simulation. The class provides the naming
  /// and parenting logic which is mandated by the
  /// IEntity interface.
  /// </summary>
  /// <typeparam name="T">
  /// The parameter T is used to specify the type
  /// of the parent object, to reduce the nessesary
  /// amount of casting. If an entity can have 
  /// multiple types of parent, the parameter T
  /// must be set to object.
  /// </typeparam>
  public abstract class AbstractEntity<T> : CommonEntity, IEntity<T> 
  {
    /// <summary>
    /// The parent entity. Null iff
    /// entity is at top level.
    /// </summary>
    protected readonly T parent;

    /// <summary>
    /// The parent of the entity.
    /// </summary>
    /// <value>parent of this entity</value>
    public T Parent
    {
      get
      {
        return parent;
      }
    }

    /// <summary>
    /// Convenience method to get
    /// the parent component as a
    /// common entity
    /// </summary>
    /// <value></value>
    internal override CommonEntity ParentAsCommonEntity
    {
      get
      {
        return parent as CommonEntity;
      }
    }

    /// <summary>
    /// Returns a reference to the 
    /// parenting model or null if no 
    /// model parent could be found
    /// </summary>
    /// <value></value>
    public Model Model
    {
      get
      {
        CommonEntity entity = this;

        while (entity != null && !(entity is Model))
        {
          entity = entity.ParentAsCommonEntity;
        }

        return entity as Model;
      }
    }

    /// <summary>
    /// Computes the fully qualified name of the entity
    /// which should be unique among all model entities
    /// </summary>
    /// <value>the fully qualified name of the entity</value>
    public string Name
    {
      get
      {
        return parent == null || parent is Model ?
            localName :  
            (parent as CommonEntity).LocalName + "." + localName;
      }

			protected set
			{
				localName = value;
			}
    }

    /// <summary>
    /// Constructs a new AbstractEntity with the specified
    /// parent and with the given local name.
    /// </summary>
    /// <param name="parent">the parent of the entity</param>
    /// <param name="localName">the local name of the entity</param>
		protected AbstractEntity(T parent, String localName, Position startPosition, Position endPosition)
    {
      this.parent = parent;
      this.localName = localName;
      this.startPosition = startPosition;
			this.endPosition = endPosition;
    }

    protected AbstractEntity(T parent, String localName)
      : this(parent, localName, null, null)
    {
    }

    /// <summary>
    /// Constructs a new AbstractEntity with an 
    /// autogenerated name and the specified parent.
    /// </summary>
    /// <param name="parent">the parent of the entity</param>
    protected AbstractEntity(T parent)
    {
      this.parent = parent;
      this.localName = this.GetType().Name.ToLower() + id;
    }

    public override string ToString()
    {
      return this.Name;
    }

  }
}